package cloud.xuxiaowei.gateway.filter;

import cloud.xuxiaowei.core.properties.BlackListProperties;
import cloud.xuxiaowei.utils.IpAddressMatcher;
import cloud.xuxiaowei.utils.Response;
import cloud.xuxiaowei.utils.ResponseUtils;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.List;

/**
 * 黑名单 过滤器
 * <p>
 * 指定 IP、URL 等禁止访问
 *
 * @author xuxiaowei
 * @since 0.0.1
 */
@Slf4j
@Setter
@Component
public class BlackListWebFilter implements WebFilter, Ordered {

	/**
	 * 最低优先级（最大值）：0
	 * <p>
	 * 大于 0 无效
	 */
	public static final int ORDERED = Ordered.HIGHEST_PRECEDENCE + 30000;

	private int order = ORDERED;

	private BlackListProperties blackListProperties;

	@Override
	public int getOrder() {
		return this.order;
	}

	@Autowired
	public void setBlackListProperties(BlackListProperties blackListProperties) {
		this.blackListProperties = blackListProperties;
	}

	@NonNull
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, @NonNull WebFilterChain chain) {

		ServerHttpRequest request = exchange.getRequest();
		ServerHttpResponse response = exchange.getResponse();

		URI uri = request.getURI();
		String path = uri.getPath();

		InetSocketAddress remoteAddress = request.getRemoteAddress();
		InetAddress address = remoteAddress.getAddress();
		String hostAddress = address.getHostAddress();

		List<String> ipList = blackListProperties.getIpList();

		for (String ip : ipList) {
			IpAddressMatcher ipAddressMatcher = new IpAddressMatcher(ip);
			boolean matches = ipAddressMatcher.matches(hostAddress);
			if (matches) {
				Response<?> error = Response.error("当前 IP 已被列入黑名单");
				log.error(error.getMessage());
				return ResponseUtils.writeWith(response, error);
			}
		}

		AntPathMatcher antPathMatcher = new AntPathMatcher();
		List<BlackListProperties.BlackList> services = blackListProperties.getServices();
		for (BlackListProperties.BlackList service : services) {
			String name = service.getName();
			List<String> pathList = service.getPathList();

			List<String> ips = service.getIpList();
			if (ips != null) {
				for (String ip : ips) {
					IpAddressMatcher ipAddressMatcher = new IpAddressMatcher(ip);
					boolean matches = ipAddressMatcher.matches(hostAddress);
					if (matches) {
						Mono<Void> pathMatch = pathMatch(pathList, name, antPathMatcher, path, response);
						if (pathMatch != null) {
							return pathMatch;
						}
					}
				}
			}
		}

		return chain.filter(exchange);
	}

	private Mono<Void> pathMatch(List<String> pathList, String name, AntPathMatcher antPathMatcher, String path,
			ServerHttpResponse response) {
		for (String p : pathList) {
			String pattern = UriComponentsBuilder.newInstance()
				.path("/")
				.path(name)
				.path("/")
				.path(p)
				.build()
				.toString();
			boolean match = antPathMatcher.match(pattern, path);
			if (match) {
				Response<?> error = Response.error("当前路径已禁止访问");
				log.error(error.getMessage());
				return ResponseUtils.writeWith(response, error);
			}
		}
		return null;
	}

}
